Technical details
library(GeoPressureR)
library(leaflet)
library(leaflet.extras)
library(raster)
library(dplyr)
library(ggplot2)
library(kableExtra)
library(plotly)
library(GeoLocTools)
setupGeolocation()
knitr::opts_chunk$set(echo = FALSE)
load(paste0("../data/1_pressure/", params$gdl_id, "_pressure_prob.Rdata"))
load(paste0("../data/2_light/", params$gdl_id, "_light_prob.Rdata"))
load(paste0("../data/3_static/", params$gdl_id, "_static_prob.Rdata"))
load(paste0("../data/4_basic_graph/", params$gdl_id, "_basic_graph.Rdata"))
All the results produced here are generated with (1) the raw geolocator data, (2) the labeled files of pressure and light and (3) the parameters listed below.
kable(gpr) %>% scroll_box(width = "100%")
| gdl_id | crop_start | crop_end | thr_dur | extent_N | extent_W | extent_S | extent_E | map_scale | map_max_sample | map_margin | prob_map_s | prob_map_thr | shift_k | kernel_adjust | calib_lon | calib_lat | calib_1_start | calib_1_end | calib_2_start | calib_2_end | calib_2_lon | calib_2_lat | prob_light_w | thr_prob_percentile | thr_gs | RingNo | scientific_name | common_name | mass | wing_span | Color |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 16LP | 2017-01-15 | 2017-12-20 | 6 | 17 | 9 | -25 | 38 | 5 | 300 | 30 | 1.2 | 0.9 | 0 | 1.4 | 28.76931 | -22.7204 | 2017-01-15 | 2017-04-15 | 2017-11-14 | 2017-12-20 | NA | NA | 0.1 | 0.95 | 120 | NA | Halcyon senegaloides | Woodland Kingfisher | NA | NA | #FF9770 |
The labeling of pressure data is illustrated with this figure. The black dots indicates the pressure datapoint not considered in the matching. Each stationary period is illustrated by a different colored line.
pressure_na <- pam$pressure %>%
mutate(obs = ifelse(isoutliar | sta_id == 0, NA, obs))
p <- ggplot() +
geom_line(data = pam$pressure, aes(x = date, y = obs), colour = "grey") +
geom_point(data = subset(pam$pressure, isoutliar), aes(x = date, y = obs), colour = "black") +
# geom_line(data = pressure_na, aes(x = date, y = obs, color = factor(sta_id)), size = 0.5) +
geom_line(data = do.call("rbind", shortest_path_timeserie) %>% filter(sta_id > 0), aes(x = date, y = pressure0, col = factor(sta_id))) +
theme_bw() +
scale_colour_manual(values = pam$sta$col) +
scale_y_continuous(name = "Pressure(hPa)")
ggplotly(p, dynamicTicks = T) %>% layout(showlegend = F)
sp_pressure = do.call("rbind", shortest_path_timeserie) %>% filter(sta_id > 0)
sta_plot <- which(difftime(pam$sta$end,pam$sta$start,unit="days")>3)
par(mfrow=c(2,3))
for (i in seq_len(length(sta_plot))){
i_s = sta_plot[i]
pressure_s = pam$pressure %>%
filter(sta_id==i_s & !isoutliar)
err <- pressure_s %>% left_join(sp_pressure, by="date") %>%
mutate(
err = obs-pressure-mean(obs-pressure)
) %>% .$err
hist(err, freq = F, main = paste0("sta_id=",i_s, " | ",nrow(pressure_s)," dtpts | std=",round(sd(err),2)))
xfit <- seq(min(err), max(err), length = 40)
yfit <- dnorm(xfit, mean = mean(err), sd = sd(err))
lines(xfit, yfit, col = "red")
}
raw_geolight <- pam$light %>%
transmute(
Date = date,
Light = obs
)
lightImage(tagdata = raw_geolight, offset = 0)
tsimagePoints(twl$twilight,
offset = 0, pch = 16, cex = 1.2,
col = ifelse(twl$deleted, "grey20", ifelse(twl$rise, "firebrick", "cornflowerblue"))
)
abline(v = gpr$calib_2_start, lty = 1, col = "firebrick", lwd = 1.5)
abline(v = gpr$calib_1_start, lty = 1, col = "firebrick", lwd = 1.5)
abline(v = gpr$calib_2_end, lty = 2, col = "firebrick", lwd = 1.5)
abline(v = gpr$calib_1_end, lty = 2, col = "firebrick", lwd = 1.5)
The probability map resulting from light data alone can be seen below.
li_s <- list()
l <- leaflet(width = "100%") %>%
addProviderTiles(providers$Stamen.TerrainBackground) %>%
addFullscreenControl()
for (i_r in seq_len(length(light_prob))) {
i_s <- metadata(light_prob[[i_r]])$sta_id
info <- pam$sta[pam$sta$sta_id == i_s, ]
info_str <- paste0(i_s, " | ", info$start, "->", info$end)
li_s <- append(li_s, info_str)
l <- l %>% addRasterImage(light_prob[[i_r]], opacity = 0.8, colors = "OrRd", group = info_str)
}
l %>%
addCircles(lng = gpr$calib_lon, lat = gpr$calib_lat, color = "black", opacity = 1) %>%
addLayersControl(
overlayGroups = li_s,
options = layersControlOptions(collapsed = FALSE)
) %>%
hideGroup(tail(li_s, length(li_s) - 1))
We can compare light and pressure location at long stationary stopover (>5 days). By assuming the best match of the pressure to be the truth, we can plot the histogram of the zenith angle and compare to the fit of kernel density at the calibration site.
raw_geolight <- pam$light %>%
transmute(
Date = date,
Light = obs
)
dur <- unlist(lapply(pressure_prob, function(x) difftime(metadata(x)$temporal_extent[2],metadata(x)$temporal_extent[1], units = "days" )))
long_id <- which(dur>5)
par(mfrow = c(2, 3))
for (i_s in long_id){
twl_fl <- twl %>%
filter(!deleted) %>%
filter(twilight>shortest_path_timeserie[[i_s]]$date[1] & twilight<tail(shortest_path_timeserie[[i_s]]$date,1))
sun <- solar(twl_fl$twilight)
z_i <- refracted(zenith(sun, shortest_path_timeserie[[i_s]]$lon[1], shortest_path_timeserie[[i_s]]$lat[1]))
hist(z_i, freq = F, main = paste0("sta_id=",i_s, " | ",nrow(twl_fl),"twls"))
lines(fit_z, col = "red")
xlab("Zenith angle")
}
Similarly, we can plot the line of sunrise/sunset at the best match of pressure (yellow line) and compare to the raw and labeled light data.
lightImage(
tagdata = raw_geolight,
offset = gpr$shift_k / 60 / 60
)
tsimagePoints(twl$twilight,
offset = gpr$shift_k / 60 / 60, pch = 16, cex = 1.2,
col = ifelse(twl$deleted, "grey20", ifelse(twl$rise, "firebrick", "cornflowerblue"))
)
for (ts in shortest_path_timeserie){
if (!is.null(ts)){
twl_fl <- twl %>%
filter(twilight>ts$date[1] & twilight<tail(ts$date,1))
if (nrow(twl_fl)>0){
tsimageDeploymentLines(twl_fl$twilight,
lon = ts$lon[1], ts$lat[1],
offset = gpr$shift_k / 60 / 60, lwd = 3,col = adjustcolor("orange", alpha.f = 0.5))
}
}
}
To visualize the path on GeoPressureViz, you will need to also load the pressure and light probability map and align them first with the code below.
sta_marginal <- unlist(lapply(static_prob_marginal, function(x) raster::metadata(x)$sta_id))
sta_pres <- unlist(lapply(pressure_prob, function(x) raster::metadata(x)$sta_id))
sta_light <- unlist(lapply(light_prob, function(x) raster::metadata(x)$sta_id))
pressure_prob <- pressure_prob[sta_pres %in% sta_marginal]
light_prob <- light_prob[sta_light %in% sta_marginal]
The code below will open with the shortest path computed with the graph approach.
geopressureviz <- list(
pam_data = pam,
static_prob = static_prob,
static_prob_marginal = static_prob_marginal,
pressure_prob = pressure_prob,
light_prob = light_prob,
pressure_timeserie = shortest_path_timeserie
)
save(geopressureviz, file = "~/geopressureviz.RData")
shiny::runApp(system.file("geopressureviz", package = "GeoPressureR"),
launch.browser = getOption("browser")
)
| start | end | sta_id | col | duration |
|---|---|---|---|---|
| 2017-01-15 00:00:00 | 2017-04-14 18:40:00 | 1 | #1B9E77 | 89.7777778 days |
| 2017-04-15 03:30:00 | 2017-04-15 18:05:00 | 2 | #D95F02 | 0.6076389 days |
| 2017-04-16 01:55:00 | 2017-04-16 17:20:00 | 3 | #7570B3 | 0.6423611 days |
| 2017-04-16 22:15:00 | 2017-04-17 16:45:00 | 4 | #E7298A | 0.7708333 days |
| 2017-04-17 20:30:00 | 2017-04-18 16:45:00 | 5 | #66A61E | 0.8437500 days |
| 2017-04-19 02:50:00 | 2017-04-23 21:55:00 | 6 | #E6AB02 | 4.7951389 days |
| 2017-04-24 03:05:00 | 2017-05-10 16:45:00 | 7 | #A6761D | 16.5694444 days |
| 2017-05-11 03:50:00 | 2017-05-11 16:55:00 | 8 | #666666 | 0.5451389 days |
| 2017-05-12 03:35:00 | 2017-05-12 19:15:00 | 9 | #1B9E77 | 0.6527778 days |
| 2017-05-12 22:55:00 | 2017-05-14 21:50:00 | 10 | #D95F02 | 1.9548611 days |
| 2017-05-14 23:15:00 | 2017-05-15 00:55:00 | 11 | #7570B3 | 0.0694444 days |
| 2017-05-15 01:10:00 | 2017-05-16 00:15:00 | 12 | #E7298A | 0.9618056 days |
| 2017-05-16 02:05:00 | 2017-05-17 22:35:00 | 13 | #66A61E | 1.8541667 days |
| 2017-05-18 00:00:00 | 2017-05-22 23:35:00 | 14 | #E6AB02 | 4.9826389 days |
| 2017-05-23 00:25:00 | 2017-05-23 20:25:00 | 15 | #A6761D | 0.8333333 days |
| 2017-05-23 22:45:00 | 2017-05-24 01:05:00 | 16 | #666666 | 0.0972222 days |
| 2017-05-24 01:35:00 | 2017-05-25 01:40:00 | 17 | #1B9E77 | 1.0034722 days |
| 2017-05-25 02:05:00 | 2017-06-11 20:15:00 | 18 | #D95F02 | 17.7569444 days |
| 2017-06-11 22:15:00 | 2017-06-12 21:30:00 | 19 | #7570B3 | 0.9687500 days |
| 2017-06-12 22:25:00 | 2017-06-13 18:10:00 | 20 | #E7298A | 0.8229167 days |
| 2017-06-13 20:00:00 | 2017-06-14 21:50:00 | 21 | #66A61E | 1.0763889 days |
| 2017-06-14 23:10:00 | 2017-06-15 00:30:00 | 22 | #E6AB02 | 0.0555556 days |
| 2017-06-15 00:45:00 | 2017-06-15 20:30:00 | 23 | #A6761D | 0.8229167 days |
| 2017-06-15 21:20:00 | 2017-10-26 16:35:00 | 24 | #666666 | 132.8020833 days |
| 2017-10-27 03:40:00 | 2017-10-27 16:30:00 | 25 | #1B9E77 | 0.5347222 days |
| 2017-10-28 03:25:00 | 2017-10-28 16:45:00 | 26 | #D95F02 | 0.5555556 days |
| 2017-10-29 03:25:00 | 2017-10-29 18:55:00 | 27 | #7570B3 | 0.6458333 days |
| 2017-10-30 02:40:00 | 2017-10-30 19:00:00 | 28 | #E7298A | 0.6805556 days |
| 2017-10-30 21:45:00 | 2017-10-30 23:10:00 | 29 | #66A61E | 0.0590278 days |
| 2017-10-30 23:55:00 | 2017-11-04 16:55:00 | 30 | #E6AB02 | 4.7083333 days |
| 2017-11-05 01:40:00 | 2017-11-05 18:15:00 | 31 | #A6761D | 0.6909722 days |
| 2017-11-05 22:15:00 | 2017-11-06 18:25:00 | 32 | #666666 | 0.8402778 days |
| 2017-11-06 23:05:00 | 2017-11-07 22:55:00 | 33 | #1B9E77 | 0.9930556 days |
| 2017-11-08 00:10:00 | 2017-11-10 19:45:00 | 34 | #D95F02 | 2.8159722 days |
| 2017-11-10 22:15:00 | 2017-11-11 21:35:00 | 35 | #7570B3 | 0.9722222 days |
| 2017-11-11 22:55:00 | 2017-11-12 18:00:00 | 36 | #E7298A | 0.7951389 days |
| 2017-11-13 00:10:00 | 2017-11-13 17:10:00 | 37 | #66A61E | 0.7083333 days |
| 2017-11-14 01:55:00 | 2017-12-19 23:55:00 | 38 | #E6AB02 | 35.9166667 days |